<?php

declare(strict_types=1);

namespace App\Traits\Model;

use App\Constants\Admin\AdminLogCode;
use App\Model\Admin\AdminLog;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\MySqlConnection;
use Hyperf\Collection\Arr;
use Hyperf\Stringable\Str;
use Hyperf\Database\Query\Builder as QueryBuilder;

/**
 * Trait Model
 * @author zx
 * @package App\Traits\Model
 */
trait Model
{
    /**
     * 坐标字段
     * @var array
     */
    public array $geometry = [];

    /**
     * 自动添加
     * @var array
     */
    public array $insert = [];

    /**
     * 模块名称
     * @var string
     */
    protected string $modularName = '';

    /**
     * 搜索器
     * @param array $data
     * @param string $alias
     * @return mixed
     */
    public function search(array $data=[], string $alias='')
    {
        $model = static::getModel();
        foreach ($data as $key => $column) {
            $action = Str::studly($key);
            $method = "scope{$action}";
            if ('' !== $column && method_exists($this, $method)) {
                $scopeAction = lcfirst($action);
                $model = $model->$scopeAction($column, $alias, $data);
            }
        }

        return $model;
    }

    /**
     * 获取别名
     * @param string $alias
     * @return string
     */
    protected function getAlias($alias='', string $field='')
    {
        return ($alias?($alias.'.'):'').$field;
    }

    /**
     * 模型日志记录
     * @param int $type
     * @param string|null $desc
     * @param string $modularName
     */
    public function addLog(int $type, ?string $desc=null, string $modularName='')
    {
        $modularName = $modularName?:$this->modularName;
        if (!$modularName) return;

        $data = $this->getAttributes();
        $descArr = [
            AdminLogCode::TYPE_CREATE => '数据信息',
            AdminLogCode::TYPE_EXPORT => '表格信息',
            AdminLogCode::TYPE_PASSWORD => '操作',
            AdminLogCode::TYPE_LOGIN => '后台管理系统',
            AdminLogCode::TYPE_DELETE => '数据信息'
        ];

        if(!$desc) {
            if (isset($descArr[$type])) {
                $desc = $descArr[$type];
            } elseif ($type === AdminLogCode::TYPE_EDIT) {
                $desc = $this->getEditDesc();
            }
        }
        $outId = Arr::get($data, $this->primaryKey, Arr::get($this->getOriginal(), $this->primaryKey, 0));

        AdminLog::add($type, $desc, $modularName, $outId, static::class);
    }

    /**
     * 获取数据库字段
     * @return array
     */
    public function getColumns()
    {
        /** @var ConnectionResolverInterface $connectionResolver */
        $connectionResolver = $this->container->get(ConnectionResolverInterface::class);
        /** @var MySqlConnection $connection */
        $connection = $connectionResolver->connection('default');

        $schemaBuilder = $connection->getSchemaBuilder();
        $columns = $schemaBuilder->getColumnTypeListing($this->getTable());

        return $columns;
    }

    /**
     * 获取字段更改列表
     * @return string
     */
    public function getEditDesc()
    {
        $changeData = $this->getChanges(); // 修改数据
        $originData = $this->getOriginal(); // 原始数据
        $columns = $this->getColumns(); // 数据库表字段列表

        $descList = []; // 每段更改信息
        foreach ($changeData as $key => $val) {
            $exclude = [$this->getCreatedAtColumn(), $this->getUpdatedAtColumn(), $this->primaryKey]; // 需要排除的字段
            if (method_exists($this, 'getDeletedAtColumn')) {
                $exclude[] = $this->getDeletedAtColumn();
            }
            if (isset($columns[array_search($key, array_column($columns, 'column_name'))]) && !in_array($key, $exclude)) {
                $column = $columns[array_search($key, array_column($columns, 'column_name'))]; // 数据库表单个字段信息
                $commentArr = explode('|', $column['column_comment']); // 区分注释和枚举
                $commentStr = $commentArr[0]; // 字段注释
                if (!$commentStr) $commentStr = $key;
                $notesVal = ''; // 更改后字段值注释
                $originNotesVal = ''; // 原始字段值注释
                if (Arr::get($commentArr, 1)) { // 如果有json字段说明该字段为枚举字段, 获取值注释
                    $jsonArr = json_decode($commentArr[1], true);
                    if ($jsonArr && isset($jsonArr[$val])) {
                        $notesVal = "({$jsonArr[$val]})";
                        if ($originData && Arr::get($originData, $key) !== null) {
                            $originNotesVal = '('.Arr::get($jsonArr, $originData[$key]).')';
                        }
                    }
                }
                if (is_array($val) || is_object($val)) { // 更改的值为json
                    $val = json_encode($val, JSON_UNESCAPED_UNICODE);
                }
                if (is_array(Arr::get($originData, $key)) || is_object(Arr::get($originData, $key))) {
                    $originData[$key] = json_encode($originData[$key], JSON_UNESCAPED_UNICODE);
                }
                if (!$originData || Arr::get($originData, $key) === null) {
                    $descList[] = $commentStr."更改为【{$val}{$notesVal}】";
                } else {
                    $descList[] = $commentStr."【{$originData[$key]}{$originNotesVal}】更改为【{$val}{$notesVal}】";
                }
            }
        }

        return implode(',', $descList);
    }

    /**
     * 按时间查询
     * @param Builder $query
     * @param $value
     * @param string $alias
     * @param array $data
     * @throws \Exception
     */
    public function scopeAt(Builder $query, $value, string $alias='', array $data=[]):void
    {
        $value = (array)$value;
        foreach ($value as $item) {
            $startAt = "start_{$item}";
            $endAt = "end_{$item}";
            $alias = $this->getAlias($alias);
            if (!Arr::get($data, $startAt) && Arr::get($data, 'start_at')) {
                $data[$startAt] = $data['start_at'];
            }
            if (!Arr::get($data, $endAt) && Arr::get($data, 'end_at')) {
                $data[$endAt] = $data['end_at'];
            }
            if (Arr::get($data, $startAt) && Arr::get($data, $endAt)) {
                $query->where([
                    [function (QueryBuilder $builder)use($item, $data, $startAt, $alias){
                        $builder->whereDate($alias.$item, '>=', new \DateTimeImmutable($data[$startAt]));
                    }],
                    [function (QueryBuilder $builder)use($item, $data, $endAt, $alias){
                        $builder->whereDate($alias.$item, '<=', new \DateTimeImmutable($data[$endAt]));
                    }],
                ]);
            } elseif (Arr::get($data, $startAt)) {
                $query->whereDate($alias.$item, '>=', new \DateTimeImmutable($data[$startAt]));
            } elseif (Arr::get($data, $endAt)) {
                $query->whereDate($alias.$item, '<=', new \DateTimeImmutable($data[$endAt]));
            }
        }
    }

    /**
     * 查询列表
     * @param $query
     * @param $value
     * @param string $alias
     */
    public function scopeIds($query, $value, string $alias='')
    {
        $query->whereIn($this->getAlias($alias, $this->getKeyName()), (array)$value);
    }

    /**
     * 获取最大排序值
     * @param int $parentId
     * @return int|mixed
     */
    public static function getMaxSort($parentName='parent_id', $parentId=0)
    {
        $map = [];
        if ($parentName) {
            $map[] = [$parentName, '=', $parentId];
        }
        $sort = self::where($map)->max('sort');
        return $sort?:0;
    }
}
